package uk.ac.cam.bt288.DesktopApp;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.Map;

import biz.source_code.base64Coder.Base64Coder;

public class ClientHandler {
	private Socket s;
	private KeyPair k;
	private InputStream in;
	private OutputStream out;
	
	public ClientHandler(Socket socket, KeyPair kp) {
		System.out.println("New client connected");
		s = socket;
		k = kp;
		try {
			in = s.getInputStream();
			out = s.getOutputStream();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public void authenticate (Map<Key, String> m)  {
		System.out.println("Start authenticating");
		String username = null;
		byte[] appKey = k.getPublic().getEncoded();
		
		try {
			out.write(appKey);
			out.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		byte[] clientKey = new byte[1024];
		byte[] randMessage = new byte[128];
		
		int clientKeyBytes = -1;
		int messageLength = -1;
		
		//receive temporary public key from client, and a random message encrypted with app's public key
		try {
			clientKeyBytes = in.read(clientKey);
			messageLength = in.read(randMessage);
		} catch (IOException e) {
			e.printStackTrace();
		}
		String msg = null;
		
		System.err.println("clientKeyBytes: "+clientKeyBytes+"  messageLength: "+messageLength);
		System.err.println("Msg: "+new String(Base64Coder.encode(randMessage)));
		
		try {
			msg = Keys.decrypt(randMessage, k.getPrivate());
			//msg = Keys.decrypt(Keys.encrypt("abcd", k.getPublic()), k.getPrivate());
			System.err.println("Rand str: " + msg);
		} catch (Exception e) {
			e.printStackTrace();

		}
		
		//send decrypted message back to Pico, encoded with Pico's temporary public key to authenticate app
		KeyFactory rsaKeyFac;
		PublicKey pubKey = null;
		
		try {
			rsaKeyFac = KeyFactory.getInstance("RSA");
			pubKey = rsaKeyFac.generatePublic(new X509EncodedKeySpec(Arrays.copyOfRange(clientKey, 0, clientKeyBytes)));
			out.write(Keys.encrypt(msg, pubKey));
			out.flush();
		} catch (Exception e1) {
			e1.printStackTrace();
		}
		 
		//receive client's real public key
		try {
			clientKeyBytes = in.read(clientKey);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		//generate random string to authenticate client
		
		
		try {
			rsaKeyFac = KeyFactory.getInstance("RSA");
			pubKey = rsaKeyFac.generatePublic(new X509EncodedKeySpec(Arrays.copyOfRange(clientKey, 0, clientKeyBytes)));
			
			if (!m.containsKey(pubKey)) {
				System.out.println("User has not registered");
				return;
			}
			else {
				username = m.get(pubKey);
			}
			
			String randStr = Keys.nextSessionId();
			out.write(Keys.encrypt(randStr, pubKey));
			out.flush();
			System.err.println("Sent str: "+randStr);
			messageLength = in.read(randMessage);
			
			String clientAuth = Keys.decrypt(Arrays.copyOfRange(randMessage, 0, messageLength), k.getPrivate());
			
			System.err.println("Received msg: "+clientAuth);
			
			clientAuth = clientAuth.trim();
			randStr = randStr.trim();
			
			if (clientAuth.equals(randStr)) {
				System.out.println("Successfully authenticated "+username);
			}
			else {
				System.out.println("Authentication has failed");
				return;
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}
	
	
}
